#ifndef __CONFIGURE_H__
#define __CONFIGURE_H__

#include <map>
#include <sstream>
#include "Utils/Singleton.h"
#include "Utils/Util.h"

namespace RayFrame
{
  #define DEFAULT_CFG_NAME "rayframe.cfg"

  /** This class holds global configuration information composed of
      key-value pairs. **/
  class DLL_DECLARE Configure: public Singleton<Configure>,
                               public IObject
  {
  public:
    Configure();
    ~Configure(void);

    #define CONFIGURE(name, default) static char* varName_##name;
    #include "Configure.def"
    #undef CONFIGURE

    enum CfgVar
    {
      #define CONFIGURE(name, default) Cfg_##name,
      #include "Configure.def"
      #undef CONFIGURE
    };

    /** Load configuration from file
        @param filename config file name. If file not exists, it will
        create a new one with default configuration.
    **/
    void Create(STRING filename = "");
    /// Save current configuration.
    void Destroy(bool save = true);

    /// Load a default config file.
    void LoadDefaultConfigure();
    /// Save config to a file.
    bool SaveToFile(STRING filename);

    inline bool HasKey(STRING key)
    {
      return m_data.find(key) != m_data.end();
    }

    /// Set a value
    template<typename T>
    void SetValue(STRING key, T value)
    {
      STRING svalue = STRING(value);
      m_data[key] = value;
    };

    template<typename T>
    void SetValue(CfgVar key, T value)
    {
      STRING svalue = STRING(value);
      assert(m_cfgVarNames.find(key) != m_cfgVarNames.end());
      m_data[m_cfgVarNames[key]] = svalue;
    }

    /// Retrive a value
    template<typename T>
    bool GetValue(STRING key, T& value)
    {
      if(HasKey(key))
      {
        value = T(m_data[key]);
        return true;
      }
      return false;
    }

    template<typename T>
    bool GetValue(CfgVar key, T& value)
    {
      assert(m_cfgVarNames.find(key) != m_cfgVarNames.end());

      return GetValue<T>( m_cfgVarNames[key], value);
    }

    template<typename T>
    T GetValue(CfgVar var)
    {
      if(m_cfgVarNames.find(var) == m_cfgVarNames.end())
        return T("");
      return GetValue<T>(m_cfgVarNames[var]);
    }

    template<typename T>
    T GetValue(STRING key)
    {
      if(HasKey(key))
        return m_data[key];
      else
        return T("");
    }

    /// Set a number
    template<typename T>
    void SetValueNum(STRING key, T num)
    {
      std::stringstream ss;
      ss << num;
      m_data[key] = ss.str();
    }

    template<typename T>
    void SetValueNum(CfgVar key, T num)
    {
      assert(m_cfgVarNames.find(key) != m_cfgVarNames.end());
      SetValueNum<T>(m_cfgVarNames[key], num);
    }

    /// Retrive a number
    template<typename T>
    bool GetValueNum(STRING key, T& num)
    {
      std::stringstream ss;
      if(m_data.find(key) != m_data.end())
      {
        ss << m_data[key];
        if((ss >> num).fail())
          return false;
        return true;
      }
      return false;
    }

    template<typename T>
    bool GetValueNum(CfgVar key, T& num)
    {
      assert(m_cfgVarNames.find(key) != m_cfgVarNames.end());
      return GetValueNum<T>(m_cfgVarNames[key], num);
    }

    template<typename T>
    T GetValueNum(STRING key)
    {
      std::stringstream ss;
      T result;
      if(m_data.find(key) != m_data.end())
      {
        ss << m_data[key];
        if((ss >> result).fail())
          return 0;
        else
          return result;
      }
      return 0;
    }

    template<typename T>
    T GetValueNum(CfgVar key)
    {
      assert(m_cfgVarNames.find(key) != m_cfgVarNames.end());
      return GetValueNum<T>(m_cfgVarNames[key]);
    }
  protected:
    bool doLoadFromFile(STRING filename);

    void internalCreate();
    void internalDestroy();
  protected:
    typedef std::map<STRING, STRING> MapStrStr;
    typedef MapStrStr::iterator IterStrSTr;

    MapStrStr m_data;
    MapStrStr m_cfgVarDefault;

    std::map<CfgVar, STRING>  m_cfgVarNames;

    STRING                    m_filename;

    DECL_SINGLETON(Configure);
  };

#define GetCfgValue(k)        Configure::InstanceRef().GetValue<STRING>(k)
#define GetCfgValueNum(k)     Configure::InstanceRef().GetValueNum<int>(k)
#define SetCfgValue(k, v)     Configure::InstanceRef().SetValue<STRING>(k, v)
#define SetCfgValueNum(k, v)  Configure::InstanceRef().SetValueNum<int>(k, v);

#define WINDOW_X              "window_x"
#define WINDOW_Y              "window_y"
#define WINDOW_CAPTION        "window_caption"
#define WINDOW_WIDTH          "window_width"
#define WINDOW_HEIGHT         "window_height"
#define SHOW_DEBUG_INFO       "show_debug_info"
#define VSYNC                 "vsync"
#define DEFAULT_FONT          "default_font"
#define DEFAULT_FONT_SIZE     "default_font_size"
#define PARTICLE_USE_ALPHA    "particle_use_alpha"
#define CONFIGURE_KEY_COUNT   9

}

#endif
